#pragma once

#include "muduo/net/TcpServer.h"
#include "muduo/net/TcpConnection.h"
#include "muduo/net/EventLoop.h"

#include "proto/dispatcher.h"
#include "proto/codec.h"

#include "../proto_dir/log_proto.pb.h"
#include "../log_service/logger.h"

namespace ns_log
{
    using namespace ns_log_proto;
    using declareLoggerRequestPtr = std::shared_ptr<declareLoggerRequest>;
    using sinklogRequestPtr = std::shared_ptr<sinklogRequest>;

    using ProtobufCodecPtr = std::shared_ptr<ProtobufCodec>;

    // 支持多客户端的资源隔离
    class Connection
    {
    public:
        using ptr = std::shared_ptr<Connection>;
        Connection(const std::string &connection_id, const muduo::net::TcpConnectionPtr &conn, const ProtobufCodecPtr &codec)
            : _connection_id(connection_id), _conn(conn), _codec(codec)
        {
        }
        void declareLogger(const declareLoggerRequestPtr &req)
        {
            // 0. 转换日志器名称
            std::string my_logger_name = convertToLoggerName(req->logger_name());
            // 1.查找是否有该日志器
            if (getLogger(my_logger_name).get() != nullptr)
            {
                basicCommonResponse(req->rid(), true, "该日志器已经存在,但不影响后续操作");
                return;
            }
            // 2. 构造日志器
            GlobalLoggerBuilder::ptr _global_builder = std::make_shared<GlobalLoggerBuilder>();
            _global_builder->buildLoggerName(my_logger_name);

#ifdef SERVER_ASYNC
            // 统一用异步的日志打印方式，目的是业务线程，让业务线程能够去处理更多的请求
            _global_builder->buildLoggerType(LoggerType::ASYNC);
#else
            _global_builder->buildLoggerType(getLoggerType(req->logger_type()));
#endif

            _global_builder->buildLimitLevel(getLogLevel(req->limit_level()));
            _global_builder->buildFormatter(req->format_pattern());

            _global_builder->buildAsyncType(getAsyncType(req->async_type()));
            _global_builder->buildSinkMode(SinkMode::LOCAL);
            for (int i = 0; i < req->sinks_size(); i++)
            {
                sink_type fs_type = req->sinks(i).type();
                switch (fs_type)
                {
                case sink_type::StdoutSink:
                    _global_builder->buildFSLogSink<ns_log::StdoutSink>();
                    break;
                case sink_type::FileSink:
                    _global_builder->buildFSLogSink<ns_log::FileSink>(req->sinks(i).args(0));
                    break;
                case sink_type::RollSinkBySize:
                    _global_builder->buildFSLogSink<ns_log::RollSinkBySize>(req->sinks(i).args(0), std::stoi(req->sinks(i).args(1)));
                    break;
                case sink_type::SqliteSink:
                    _global_builder->buildDBLogSink<ns_log::SqliteSink>(req->sinks(i).args(0));
                    break;
                case sink_type::MySQLSink:
                    _global_builder->buildDBLogSink<ns_log::MySQLSink>(req->sinks(i).args(0));
                    break;
                default:
                    default_fatal("声明日志器时，遇到未知sink_type: %s", sink_type_Name(fs_type));
                    break;
                }
            }
            _global_builder->build();
            // 3. commonResponse
            basicCommonResponse(req->rid(), true, "一切顺利");
        }

        void sinklog(const sinklogRequestPtr &req)
        {
            // 0. 转换日志器名称
            std::string my_logger_name = convertToLoggerName(req->logger_name());
            // 1. 拿到日志器
            Logger::ptr logger = getLogger(my_logger_name);
            if (logger.get() == nullptr)
            {
                basicCommonResponse(req->rid(), false, "日志落地失败，因为当前日志器并不存在：" + my_logger_name);
                return;
            }
            for (int i = 0; i < req->message_size(); i++)
            {
                const logMessage &message = req->message(i);
                logger->log_pure(getMessage(message));
                // // 2. 进行日志打印
                // switch (message.level())
                // {
                // case logLevel::DEBUG:
                // {
                //     logger->debug(message.file(), message.line(), "%s", message.body().c_str());
                //     break;
                // }
                // case logLevel::INFO:
                // {
                //     logger->info(message.file(), message.line(), "%s", message.body().c_str());
                //     break;
                // }
                // case logLevel::WARNING:
                // {
                //     logger->warning(message.file(), message.line(), "%s", message.body().c_str());
                //     break;
                // }
                // case logLevel::ERROR:
                // {
                //     logger->error(message.file(), message.line(), "%s", message.body().c_str());
                //     break;
                // }
                // case logLevel::FATAL:
                // {
                //     logger->fatal(message.file(), message.line(), "%s", message.body().c_str());
                //     break;
                // }
                // default:
                // {
                //     logger->fatal(message.file(), message.line(), "日志等级[%s]未知，日志body: %s", logLevel_Name(message.level()).c_str(), message.body().c_str());
                //     break;
                // }
                //}
            }
            // basicCommonResponse
            basicCommonResponse(req->rid(), true, "一切顺利");
        }

    private: // 网络模块和业务模块之间通信类型的转换
        LogMessage::ptr getMessage(const logMessage &message)
        {
            return std::make_shared<LogMessage>(getLogLevel(message.level()), message.file(), message.line(), convertToLoggerName(message.logger_name()), message.body());
        }

        LoggerType getLoggerType(loggerType type)
        {
            return (type == loggerType::SYNC) ? LoggerType::SYNC : LoggerType::ASYNC;
        }

        LogLevel::value getLogLevel(logLevel level)
        {
            switch (level)
            {
            case logLevel::UNKNOWN:
                return LogLevel::value::UNKNOWN;
            case logLevel::DEBUG:
                return LogLevel::value::DEBUG;
            case logLevel::INFO:
                return LogLevel::value::INFO;
            case logLevel::WARNING:
                return LogLevel::value::WARNING;
            case logLevel::ERROR:
                return LogLevel::value::ERROR;
            case logLevel::FATAL:
                return LogLevel::value::FATAL;
            case logLevel::OFF:
                return LogLevel::value::OFF;
            default:
                default_fatal("未知logLevel：%s", logLevel_Name(level));
                return LogLevel::value::UNKNOWN;
            }
        }

        AsyncType getAsyncType(asyncType type)
        {
            return (type == asyncType::ASYNC_SAFE) ? AsyncType::ASYNC_SAFE : AsyncType::ASYNC_UNSAFE;
        }

        std::string convertToLoggerName(const std::string &old_logger_name)
        {
            return _connection_id + " " + old_logger_name;
        }

        void basicCommonResponse(const std::string &rid, bool ok, const std::string &err_msg)
        {
            // 构建resp并发出
            commonResponse resp;
            resp.set_rid(rid);
            resp.set_ok(ok);
            resp.set_err_msg(err_msg);
            _codec->send(_conn, resp);
        }

    private:
        std::string _connection_id;
        muduo::net::TcpConnectionPtr _conn;
        ProtobufCodecPtr _codec;
    };

    class ConnectionManager
    {
    public:
        using ptr = std::shared_ptr<ConnectionManager>;

        void OpenConnection(const std::string &connection_id, const muduo::net::TcpConnectionPtr &conn, const ProtobufCodecPtr &codec)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            if (_myconn_map.count(conn))
                return;
            _myconn_map.insert(std::make_pair(conn, std::make_shared<Connection>(connection_id, conn, codec)));
        }

        void CloseConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            _myconn_map.erase(conn);
        }

        Connection::ptr getConnection(const muduo::net::TcpConnectionPtr &conn)
        {
            std::unique_lock<std::mutex> ulock(_mutex);
            auto iter = _myconn_map.find(conn);
            if (iter == _myconn_map.end())
                return Connection::ptr();
            return iter->second;
        }

    private:
        std::mutex _mutex;
        std::unordered_map<muduo::net::TcpConnectionPtr, Connection::ptr> _myconn_map;
    };

}
